#ifndef __M_BINDING_H__
    #define __M_BINDING_H__
#include <iostream>
#include <string>
#include <unordered_map>
#include <memory>
#include <vector>
#include <mutex>
#include <sstream>
#include "../mqcommon/mq_msg.pb.h"
#include "../mqcommon/mq_loger.hpp"
#include "../mqcommon/mq_helper.hpp"

namespace mq{

struct Binding{
    std::string exchange_name;//交换机名称
    std::string msgqueue_name;//队列 名称
    std::string binding_key;

    using ptr = std::shared_ptr<Binding>;
    Binding(){}//默认构造
    Binding(const std::string& ename,const std::string& qname,const std::string& key):exchange_name(ename),msgqueue_name(qname),binding_key(key){}
};

//MsgQueueBindingMap绑定信息：  队列 ： 绑定信息
using MsgQueueBindingMap = std::unordered_map<std::string,Binding::ptr>;
//BindingMap：交换机 ： 相关队列的绑定信息
using BindingMap = std::unordered_map<std::string,MsgQueueBindingMap>;

class BindingMapper{
private:
    mqhelper::SqliteHelper _sqlit_hepler;
public:
    using ptr = std::shared_ptr<BindingMapper>;

    BindingMapper(const std::string& _dbfile):_sqlit_hepler(_dbfile){
            std::string dir = mqhelper::FileHelper::parentDirectory(_dbfile);
        if(dir != "./"){
            mqhelper::FileHelper::createDirectory(dir);
        } 
        assert(_sqlit_hepler.open());//打开数据库
        //创建表
        createTable();
    }
    void createTable(){
        std::stringstream sql;
        sql << "create table if not exists binding_table(";
        sql << "exchange_name varchar(32),";
        sql << "queue_name varchar(32),";
        sql << "binding_key varchar(128));";
        assert(_sqlit_hepler.exec(sql.str(),nullptr,nullptr));
    }

    void removeTable(){//直接删除表
        std::stringstream sql;
        sql << "drop table binding_table;";//DROP [TEMPORARY] TABLE [IF EXISTS] tbl_name [, tbl_name] ...
        assert(_sqlit_hepler.exec(sql.str(),nullptr,nullptr));
    }

    bool insert(const Binding::ptr& binding){
        std::stringstream sql;
        //insert into table (....column )values(值);
        sql << "insert into binding_table values(";
        sql << "'" << binding->exchange_name << "', ";
        sql << "'" << binding->msgqueue_name << "', ";
        sql << "'" << binding->binding_key << "' );";
        return _sqlit_hepler.exec(sql.str(),nullptr,nullptr);
    }

    //删除指定的绑定信息
    bool remove(const std::string& ename,const std::string& qname){
        std::stringstream sql;
        sql << "delete from binding_table where ";
        sql << "exchange_name = '" << ename << "' and ";
        sql << "queue_name = '" << qname << "';";
        return _sqlit_hepler.exec(sql.str(),nullptr,nullptr);
    }

    //删除指定交换机相关的绑定信息
    bool removeExchangeBinding(const std::string& ename){
        std::stringstream sql;
        sql << "delete from binding_table where ";
        sql << "exchange_name = '" << ename << "'; ";
        return _sqlit_hepler.exec(sql.str(),nullptr,nullptr);
    }

    //删除指定队列相关的绑定信息
    bool removeQueueBinding(const std::string& qname){
        std::stringstream sql;
        sql << "delete from binding_table where ";
        sql << "queue_name = '" << qname << "'); ";
        return _sqlit_hepler.exec(sql.str(),nullptr,nullptr);
    }



    //恢复所有交换机数据
    BindingMap covery(){
        std::stringstream sql;
        sql << "select exchange_name,queue_name,binding_key from binding_table;";

        BindingMap result;
        _sqlit_hepler.exec(sql.str(),&selectCallback,&result);
        return result;

    }



private:
//注意 this 指针导致的类型不同 所以要定义为static的
    static int selectCallback(void* args,int num,char** row,char** col){
        //1. 类型转换为 BindingMap*
        BindingMap* res = (BindingMap*)args;
        
        //2. 获取数组中的值,此处不同于之前，现在有两个map，所以我们得注意保证这同时处理到两个map中的数据
        MsgQueueBindingMap& qmap = (*res)[row[0]];
        //row 0 得到的是exchange_name
        //使用引用是因为这样能得到，于与res相关的MsgQueueBindingMap，并且保证当MsgQueueBindingMap不存在时会自己创建，而非返回空

        //3. 创建绑定信息的智能指针，给到后面的插入
        std::shared_ptr<Binding> bp = std::make_shared<Binding>(row[0],row[1],row[2]);

        //4. 将数据 插入到 qmap（也就是插入到交换机相关的队列绑定信息中）
        qmap.insert(std::make_pair(bp->msgqueue_name,bp));

        //5. 再将qmap插入到result中完成插入{交换机名，相关绑定信息}
        res->insert(std::make_pair(bp->exchange_name,qmap));
        return 0;//返回0表示正常
    }
};//BindingMapper


class BindingManager{
private:
    std::mutex _mutex;
    BindingMapper::ptr _mapper;//持久类管理对象
    BindingMap _bindings;//内存级 {交换机 ： 绑定信息} map 
public:
    using ptr = std::shared_ptr<BindingManager>;
    
    BindingManager(const std::string& _dbfile){
        _mapper = std::make_shared<BindingMapper>(_dbfile);
    }

    bool bind(const std::string& ename,const std::string& qname,const std::string& key,bool durable){
        //1. 加锁
        std::unique_lock<std::mutex> lock(_mutex);
        //2. 查找绑定是否已存在,查找方法：先判断交换机是否存在，其次判断交换机对应的绑定信息是否存在
        auto eit = _bindings.find(ename);
        if(eit != _bindings.end()){
            auto qit = eit->second;
            if(qit.find(qname) != qit.end()){
                //若不等，就表示都找到了，也就表示该绑定是已经存在的就不用创建了
                return true;//返回true即可
            }
        }
 

        Binding::ptr bp = std::make_shared<Binding>(ename,qname,key);//创建智能对象变量
        //插入对象并返回 绑定信息的引用
        MsgQueueBindingMap& qbmap = _bindings[ename];

        //对绑定信息对象 赋值 {队列，绑定信息}
        qbmap.insert({qname,bp});

        //交换机管理对象 赋值 {交换机名，相关的绑定信息}
        _bindings.insert({ename,qbmap});
        if(durable){//durable 持久化标志
            bool ret = _mapper->insert(bp);
            if(!ret){//如果插入错误则返回false
                return false;
            }
        }
        return true;
    }
    
    bool unbind(const std::string& ename,const std::string& qname){
        //1. 加锁
        std::unique_lock<std::mutex> lock(_mutex);
        //2. 判断绑定是否存在，若不存在则直接返回
        auto eit = _bindings.find(ename);
        if(eit == _bindings.end()){
            auto qit = eit->second;
            if(qit.find(qname) == qit.end()){
                //若等，就表示没找到了，也就表示该绑定是不存在，就不用解绑了
                return false;//返回
            }
        }
        //到达此处表示的该绑定是 存在的！

        _mapper->remove(ename,qname);//就不durable判断了，直接删除即可
        _bindings.erase(ename);//直接使用容器的删除
        return true;
    }

//删除交换机相关的绑定信息
    void removeExchangeBindings(const std::string& ename){
        //1. 加锁
        std::unique_lock<std::mutex> lock(_mutex);
        //2. 就不判断了直接删除，不存在就删除失败即可
        _mapper->removeExchangeBinding(ename);
        _bindings.erase(ename);
    }

//删除队列机相关的绑定信息
    void removeQueueBindings(const std::string& qname){
          //1. 加锁
        std::unique_lock<std::mutex> lock(_mutex);
        // 遍历所有交换机，因为任何一个交换机都能同时绑定多个队列，或者说一个队列能绑定多个交换机
        //所以需要遍历交换机，找到所有与该队列有关的交换机，然后都对其进行删除操作
        for(auto& mp : _bindings){
            if(mp.second.find(qname) != mp.second.end()){
                //若不等则表示找到了，进行删除即可
                _mapper->remove(mp.first,qname);//删除数据库中的持久性数据
                
                //其中mp是一个一个 kv键值对，k存的是交换机名称，v存到是其相关的队列绑定信息map
                //second得到相关队列绑定信息的map，然后进行相关对于队列的删除
                mp.second.erase(qname);//对其中的队列进行删除
            }
        }
    }

//获得指定交换机绑定信息相关的队列
    MsgQueueBindingMap getExchangeBindings(const std::string& ename){
        //1. 加锁
        std::unique_lock<std::mutex> lock(_mutex);
        //2. 查找，找到返回，找不到返回空的对象
        auto eit = _bindings.find(ename);
        if(eit == _bindings.end()){
                return MsgQueueBindingMap();//返回空的即可
        }
        return eit->second;
    }

    Binding::ptr getBinding(const std::string& ename,const std::string& qname,const std::string& key){
        //1. 加锁
        std::unique_lock<std::mutex> lock(_mutex);
        //2. 查找，找到返回，找不到返回空的对象
        auto eit = _bindings.find(ename);
        if(eit != _bindings.end()){
            auto qit = eit->second;
            if(qit.find(qname) != qit.end()){
                //若同时都不等，则表示是存在的，则返回即可
                return qit.find(qname)->second;
            }
        }
        return Binding::ptr();
    }

    bool exists(const std::string& ename,const std::string& qname){
        //1. 加锁
        std::unique_lock<std::mutex> lock(_mutex);
        //2. 查找，找到返回，找不到返回空的对象
        auto eit = _bindings.find(ename);
        if(eit != _bindings.end()){
            auto qit = eit->second;
            if(qit.find(qname) != qit.end()){
                //若同时都不等，则表示是存在的，则返回即可
                return true;
            }
        }
        return false;
    }

    size_t size(){
        //注意其中不能直接返回_binding.size，而是要判断所有交换机中所有队列的绑定关系
        //1.加锁
        std::unique_lock<std::mutex> lock(_mutex);
        
        //2. 计数器：count
        size_t count = 0;
        //遍历
        for(auto& emp : _bindings){
            //emp是_bindings所有交换机map的pair
            count += emp.second.size();//emp.second.size()得到就是每个交换机的绑定信息map中队列的个数
        }
        return count;
    }

    void clear(){
        //1.加锁
        std::unique_lock<std::mutex> lock(_mutex);

        _mapper->removeTable();//直接删除表
        _bindings.clear();//去除所有
    }

};

}//mq

#endif